﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using PENet;
using System.Runtime.InteropServices;
using PENet.Exceptions;

namespace PENet
{
    public class PENet
    {
        #region INITIALIZATION

        public PENet(String filePath)
        {
            this.FileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
        }
        public PENet(Stream fileStream)
        {
            this.FileStream = FileStream;
        }

        #endregion

        #region CONTROLS

        private Boolean _isread_DOSHeader = false;
        private Boolean _isread_NTHeader = false;
        private Boolean _isread_SectionHeader = false;
        private Boolean _isread_SectionData = false;

        /// <summary>
        /// Resets the current state of PE.Net instance
        /// </summary>
        public void Reset()
        {
            _isread_DOSHeader = _isread_NTHeader = _isread_SectionData = _isread_SectionHeader = false;
        }

        public void CloseStream()
        {
            FileStream.Close();
        }

        #endregion

        #region PROPERTIES

        //***************************************************************//
        private Stream FileStream
        {
            get;
            set;
        }

        //***************************************************************//
        public Boolean Is32BitPE
        {
            get
            {
                if (NTHeader.FileHeader.Machine == (UInt16)PEStructures.IMAGE_MACHINE_TYPES_ENUM.IMAGE_FILE_MACHINE_I386)
                    return true;
                else return false;
            }
        }

        //***************************************************************//
        private byte[] _codeSectionData;
        public byte[] CodeSectionData
        {
            get
            {
                getCodeSection();
                return _codeSectionData;
            }
        }

        //***************************************************************//
        private PEStructures.IMAGE_DOS_HEADER _DosHeader;
        public PEStructures.IMAGE_DOS_HEADER DOSHeader
        {
            get
            {
                GetDosHeader();
                return _DosHeader;
            }
        }

        //***************************************************************//
        private PEStructures.IMAGE_NT_HEADERS _NTHeader;
        public PEStructures.IMAGE_NT_HEADERS NTHeader
        {
            get
            {
                GetNTHeader();
                return _NTHeader;
            }
        }

        //***************************************************************//
        private PEStructures.IMAGE_SECTION_HEADER_RECORD[] _SecionHeader;
        public PEStructures.IMAGE_SECTION_HEADER_RECORD[] SectionHeader
        {
            get
            {
                GetSectionHeader();
                return _SecionHeader;
            }
        }

        //***************************************************************//
        private List<byte[]> _SectionData;
        public List<byte[]> SectionData
        {
            get
            {
                GetSectionsRawData();
                return _SectionData;
            }
        }

        //***************************************************************//
        private PEStructures.IMAGE_DEBUG_DIRECTORY[] _DebugDirectory;
        public PEStructures.IMAGE_DEBUG_DIRECTORY[] DebugTable
        {
            get
            {
                getDebugDirectory();
                return _DebugDirectory;
            }
        }

        //***************************************************************//
        private PEStructures.IMAGE_EXPORT_DIRECTORY _ExportDirectory;
        public PEStructures.IMAGE_EXPORT_DIRECTORY ExportTable
        {
            get
            {
                getExportDirectory();
                return _ExportDirectory;
            }
        }

        //***************************************************************//
        private PEStructures.IMAGE_IMPORT_DIRECTORY _ImportDirectory;
        public PEStructures.IMAGE_IMPORT_DIRECTORY ImportTable
        {
            get
            {
                getImportDirectory();
                return _ImportDirectory;
            }
        }

        //***************************************************************//
        private PEStructures.IMAGE_EXCEPTION _ExceptionDirectory;
        public PEStructures.IMAGE_EXCEPTION ExceptionTable
        {
            get
            {
                getExceptionDirectoryx86();
                return _ExceptionDirectory;
            }
        }

        //***************************************************************//
        private PEStructures.IMAGE_RELOCATION_ENTRY[] _RelocationDirectory;
        public PEStructures.IMAGE_RELOCATION_ENTRY[] RelocationTable
        {
            get
            {
                getRelocationDirectory();
                return _RelocationDirectory;
            }
        }

        //***************************************************************//
        private PEStructures.IMAGE_TLS_DIRECTORY _TLSDirectory;
        public PEStructures.IMAGE_TLS_DIRECTORY TLS
        {
            get
            {
                getTLSDirectory();
                return _TLSDirectory;
            }
        }

        //***************************************************************//
        private PEStructures.IMAGE_LOAD_CONFIG_DIRECTORY _LoadConfigDirectory;
        public PEStructures.IMAGE_LOAD_CONFIG_DIRECTORY LoadConfigDirectory
        {
            get
            {
                getLoadConfigDirectory();
                return _LoadConfigDirectory;
            }
        }

        //***************************************************************//
        private PEStructures.IMAGE_CLR _CLRDirectory;
        public PEStructures.IMAGE_CLR CLRDirectory
        {
            get
            {
                getCLRDirectory();
                return _CLRDirectory;
            }
        }

        //***************************************************************//
        private PEStructures.IMAGE_RESOURCE_DIRECTORY _ResourceDirectory;
        public PEStructures.IMAGE_RESOURCE_DIRECTORY ResourceDirectory
        {
            get
            {
                getResourceDirectory();
                return _ResourceDirectory;
            }
        }

        #endregion

        #region METHODS

        /// <summary>
        /// Retreives the DOS header of the PE file
        /// </summary>
        /// <returns></returns>
        private PEStructures.IMAGE_DOS_HEADER GetDosHeader()
        {
            if (!_isread_DOSHeader)
            {
                PEStructures.IMAGE_DOS_HEADER dos_header = new PEStructures.IMAGE_DOS_HEADER();
                int size = Marshal.SizeOf(typeof(PEStructures.IMAGE_DOS_HEADER));
                byte[] buffer = new byte[size];
                FileStream.Seek(0, SeekOrigin.Begin);
                int read = FileStream.Read(buffer, 0, size);
                if (read == size)
                {
                    GCHandle hndl = GCHandle.Alloc(buffer, GCHandleType.Pinned);
                    if (hndl.IsAllocated)
                    {
                        dos_header = (PEStructures.IMAGE_DOS_HEADER)Marshal.PtrToStructure(hndl.AddrOfPinnedObject(), typeof(PEStructures.IMAGE_DOS_HEADER));
                        _DosHeader = dos_header;
                        hndl.Free();
                        _isread_DOSHeader = true;
                        return dos_header;
                    }
                    else throw new MemoryAllocationException();
                }
                else
                {
                    ErrorReadingDosHeaderException err = new ErrorReadingDosHeaderException();
                    err.ErrorType = DosHeaderErrorType.SizeLessThanDosHeader;
                    throw err;
                }
            }
            else return _DosHeader;
        }
        //////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Retreives the NT File header and Optional header of the PE file
        /// </summary>
        /// <returns></returns>
        private PEStructures.IMAGE_NT_HEADERS GetNTHeader()
        {
            if (!_isread_NTHeader)
            {
                GetDosHeader();
                FileStream.Seek(DOSHeader.e_lfanew, SeekOrigin.Begin);
                PEStructures.IMAGE_NT_HEADERS nt_header = new PEStructures.IMAGE_NT_HEADERS();
                //nt_header.OptionalHeader.DataDirectory = new PEStructures.IMAGE_DATA_DIRECTORY[16];
                byte[] buffer = new byte[4];
                int read = FileStream.Read(buffer, 0, 4);
                if (read == 4)
                {
                    nt_header.Signature = BitConverter.ToUInt32(buffer, 0);
                    int size = Marshal.SizeOf(typeof(PEStructures.IMAGE_FILE_HEADER));
                    buffer = new byte[size];
                    read = FileStream.Read(buffer, 0, size);
                    if (read == size)
                    {
                        GCHandle hndl = GCHandle.Alloc(buffer, GCHandleType.Pinned);
                        if (hndl.IsAllocated)
                        {
                            nt_header.FileHeader = (PEStructures.IMAGE_FILE_HEADER)Marshal.PtrToStructure(hndl.AddrOfPinnedObject(), typeof(PEStructures.IMAGE_FILE_HEADER));
                            hndl.Free();

                            size = nt_header.FileHeader.SizeOfOptionalHeader;
                            buffer = new byte[size];
                            read = FileStream.Read(buffer, 0, size);
                            if (read == size)
                            {
                                hndl = GCHandle.Alloc(buffer, GCHandleType.Pinned);
                                if (hndl.IsAllocated)
                                {
                                    nt_header.OptionalHeader = (PEStructures.IMAGE_OPTIONAL_HEADER)Marshal.PtrToStructure(hndl.AddrOfPinnedObject(), typeof(PEStructures.IMAGE_OPTIONAL_HEADER));
                                    hndl.Free();
                                    _NTHeader = nt_header;
                                    _isread_NTHeader = true;
                                    return nt_header;
                                }
                                else throw new MemoryAllocationException();
                            }
                            else throw new ErrorReadingNTHeaderException();
                        }
                        else throw new MemoryAllocationException();
                    }
                    else throw new ErrorReadingNTHeaderException();
                }
                else
                    throw new ErrorReadingNTHeaderException();
            }
            else return _NTHeader;
        }
        //////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Retreives the list of sections from the PE file
        /// </summary>
        /// <returns></returns>
        private PEStructures.IMAGE_SECTION_HEADER_RECORD[] GetSectionHeader()
        {
            if (!_isread_SectionHeader)
            {
                GetNTHeader();
                FileStream.Seek(DOSHeader.e_lfanew + Marshal.SizeOf(typeof(PEStructures.IMAGE_NT_HEADERS)), SeekOrigin.Begin);
                PEStructures.IMAGE_SECTION_HEADER_RECORD[] sectionHeader = new PEStructures.IMAGE_SECTION_HEADER_RECORD[NTHeader.FileHeader.NumberOfSections];
                for (int i = 0; i < NTHeader.FileHeader.NumberOfSections; i++)
                {
                    int size = Marshal.SizeOf(typeof(PEStructures.IMAGE_SECTION_HEADER_RECORD));
                    byte[] buffer = new byte[size];
                    int read = FileStream.Read(buffer, 0, size);
                    if (read == size)
                    {
                        GCHandle hndl = GCHandle.Alloc(buffer, GCHandleType.Pinned);
                        if (hndl.IsAllocated)
                        {
                            sectionHeader[i] = (PEStructures.IMAGE_SECTION_HEADER_RECORD)Marshal.PtrToStructure(hndl.AddrOfPinnedObject(), typeof(PEStructures.IMAGE_SECTION_HEADER_RECORD));
                            hndl.Free();
                        }
                        else throw new MemoryAllocationException();
                    }
                    else throw new ErrorReadingSectionHeaderException();
                }
                _SecionHeader = sectionHeader;
                _isread_SectionHeader = true;
                return sectionHeader;
            }
            else return _SecionHeader;
        }
        //////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Retreives the raw data from each of sections appearing within the PE file
        /// </summary>
        /// <returns></returns>
        private List<byte[]> GetSectionsRawData()
        {
            if (!_isread_SectionData)
            {
                GetSectionHeader();
                List<byte[]> result = new List<byte[]>();
                for (int i = 0; i < NTHeader.FileHeader.NumberOfSections; i++)
                {
                    int size = (int)SectionHeader[i].SizeOfRawData;
                    byte[] buffer = new byte[size];
                    FileStream.Seek(SectionHeader[i].PointerToRawData, SeekOrigin.Begin);
                    if (size > 0)
                    {
                        int read = FileStream.Read(buffer, 0, size);
                        if (read == size)
                        {
                            result.Add(buffer);
                        }
                        else throw new ErrorReadingSectionDataException();
                    }
                    else result.Add(null);
                }
                _SectionData = result;
                _isread_SectionData = true;
                return result;
            }
            else return _SectionData;
        }
        //////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Retreives the CODE section according to the location of EntryPoint
        /// </summary>
        /// <returns></returns>
        private byte[] getCodeSection()
        {
            GetSectionHeader();
            byte[] result = null;
            UInt32 ep = NTHeader.OptionalHeader.AddressOfEntryPoint;
            Boolean found = false;
            int i = 0;
            for (; !found && i < NTHeader.FileHeader.NumberOfSections; i++)
            {
                if (ep >= SectionHeader[i].VirtualAddress && ep <= (SectionHeader[i].VirtualAddress + SectionHeader[i].VirtualSize))
                    found = true;
            }
            if (found)
            {
                i--;
                int size = (int)SectionHeader[i].SizeOfRawData;
                byte[] buffer = new byte[size];
                FileStream.Seek(SectionHeader[i].PointerToRawData, SeekOrigin.Begin);
                if (size > 0)
                {
                    int read = FileStream.Read(buffer, 0, size);
                    if (read == size)
                    {
                        result = buffer;
                        _codeSectionData = result;
                    }
                    else throw new ErrorReadingSectionDataException();
                }
                else result = null;
            }
            return result;
        }
        //////////////////////////////////////////////////////////////////////////////////

        private PEStructures.IMAGE_DEBUG_DIRECTORY[] getDebugDirectory()
        {
            GetSectionsRawData();
            int index = (int)PEStructures.IMAGE_DIRECTORY_TYPES_ENUM.IMAGE_DIRECTORY_ENTRY_DEBUG;
            if (NTHeader.OptionalHeader.DataDirectory[index].VirtualAddress != 0x00)
            {
                int size = Marshal.SizeOf(typeof(PEStructures.IMAGE_DEBUG_DIRECTORY));
                int number = (int)NTHeader.OptionalHeader.DataDirectory[index].Size / size;
                PEStructures.IMAGE_DEBUG_DIRECTORY[] table = new PEStructures.IMAGE_DEBUG_DIRECTORY[number];
                long start = RelativeVirtualAddresstoPhysicalAddress(SectionHeader, NTHeader.OptionalHeader.DataDirectory[index].VirtualAddress);
                if (start >= 0)
                {
                    FileStream.Seek(start, SeekOrigin.Begin);
                    for (int i = 0; i < number; i++)
                    {
                        byte[] buffer = new byte[size];
                        int read = FileStream.Read(buffer, 0, size);
                        if (read == size)
                        {
                            GCHandle hndl = GCHandle.Alloc(buffer, GCHandleType.Pinned);
                            if (hndl.IsAllocated)
                            {
                                table[i] = (PEStructures.IMAGE_DEBUG_DIRECTORY)Marshal.PtrToStructure(hndl.AddrOfPinnedObject(), typeof(PEStructures.IMAGE_DEBUG_DIRECTORY));
                                hndl.Free();
                            }
                            else throw new MemoryAllocationException();
                        }
                        else throw new ErrorReadingDictionaryDataException();
                    }
                    _DebugDirectory = table;
                    return table;
                }
                else throw new ErrorReadingDictionaryDataException();
            }
            else throw new NoDebugDirectoryException();
        }
        //////////////////////////////////////////////////////////////////////////////////

        private PEStructures.IMAGE_EXPORT_DIRECTORY getExportDirectory()
        {
            GetSectionsRawData();
            PEStructures.IMAGE_EXPORT_DIRECTORY exportdir = new PEStructures.IMAGE_EXPORT_DIRECTORY();
            int index = (int)PEStructures.IMAGE_DIRECTORY_TYPES_ENUM.IMAGE_DIRECTORY_ENTRY_EXPORT;
            if (NTHeader.OptionalHeader.DataDirectory[index].VirtualAddress != 0x00)
            {
                int size = Marshal.SizeOf(typeof(PEStructures.IMAGE_EXPORT_DIRECTORY_TABLE));
                long start = RelativeVirtualAddresstoPhysicalAddress(SectionHeader, NTHeader.OptionalHeader.DataDirectory[index].VirtualAddress);
                if (start >= 0)
                {
                    FileStream.Seek(start, SeekOrigin.Begin);
                    byte[] buffer = new byte[size];
                    int read = FileStream.Read(buffer, 0, size);
                    if (read == size)
                    {
                        GCHandle hndl = GCHandle.Alloc(buffer, GCHandleType.Pinned);
                        if (hndl.IsAllocated)
                        {
                            exportdir.ExportDirectoryTable = (PEStructures.IMAGE_EXPORT_DIRECTORY_TABLE)Marshal.PtrToStructure(hndl.AddrOfPinnedObject(), typeof(PEStructures.IMAGE_EXPORT_DIRECTORY_TABLE));

                            ///////////////////////////////////////
                            // extracting the export address table
                            PEStructures.IMAGE_EXPORT_ADDRESS_TABLE[] exportaddress =
                                new PEStructures.IMAGE_EXPORT_ADDRESS_TABLE[exportdir.ExportDirectoryTable.NumberOfFunctions];
                            start = RelativeVirtualAddresstoPhysicalAddress(SectionHeader, exportdir.ExportDirectoryTable.AddressOfFunctions);
                            FileStream.Seek(start, SeekOrigin.Begin);
                            size = Marshal.SizeOf(typeof(PEStructures.IMAGE_EXPORT_ADDRESS_TABLE));
                            for (int i = 0; i < exportdir.ExportDirectoryTable.NumberOfFunctions; i++)
                            {
                                buffer = new byte[size];
                                read = FileStream.Read(buffer, 0, size);
                                if (read == size)
                                {
                                    hndl = GCHandle.Alloc(buffer, GCHandleType.Pinned);
                                    if (hndl.IsAllocated)
                                    {
                                        exportaddress[i] = (PEStructures.IMAGE_EXPORT_ADDRESS_TABLE)
                                            Marshal.PtrToStructure(hndl.AddrOfPinnedObject(), typeof(PEStructures.IMAGE_EXPORT_ADDRESS_TABLE));
                                    }
                                    else throw new MemoryAllocationException();
                                }
                                else throw new ErrorReadingDictionaryDataException();
                            }
                            exportdir.ExportAddressTable = exportaddress;

                            ///////////////////////////////////////
                            // extracting the name pointer table
                            UInt32[] nameptr = new UInt32[exportdir.ExportDirectoryTable.NumberOfNames];
                            start = RelativeVirtualAddresstoPhysicalAddress(SectionHeader, exportdir.ExportDirectoryTable.AddressOfNames);
                            FileStream.Seek(start, SeekOrigin.Begin);
                            size = 4;
                            for (int i = 0; i < exportdir.ExportDirectoryTable.NumberOfNames; i++)
                            {
                                buffer = new byte[size];
                                read = FileStream.Read(buffer, 0, size);
                                if (read == size)
                                {
                                    nameptr[i] = BitConverter.ToUInt32(buffer, 0);
                                }
                                else throw new ErrorReadingDictionaryDataException();
                            }
                            exportdir.ExportNamePointerTable = nameptr;

                            ///////////////////////////////////////
                            // extracting the name pointer table
                            UInt16[] ordinalptr = new UInt16[exportdir.ExportDirectoryTable.NumberOfNames];
                            start = RelativeVirtualAddresstoPhysicalAddress(SectionHeader, exportdir.ExportDirectoryTable.AddressOfNameOrdinals);
                            FileStream.Seek(start, SeekOrigin.Begin);
                            size = 2;
                            for (int i = 0; i < exportdir.ExportDirectoryTable.NumberOfNames; i++)
                            {
                                buffer = new byte[size];
                                read = FileStream.Read(buffer, 0, size);
                                if (read == size)
                                {
                                    ordinalptr[i] = BitConverter.ToUInt16(buffer, 0);
                                }
                                else throw new ErrorReadingDictionaryDataException();
                            }
                            exportdir.ExportOrdinalTable = ordinalptr;

                            ///////////////////////////////////////
                            // extracting the name pointer table
                            if (exportdir.ExportDirectoryTable.Name > 0)
                            {
                                String[] nametable = new String[exportdir.ExportDirectoryTable.NumberOfNames];
                                FileStream.Seek(start, SeekOrigin.Begin);
                                for (int i = 0; i < exportdir.ExportDirectoryTable.NumberOfNames; i++)
                                {
                                    start = RelativeVirtualAddresstoPhysicalAddress(SectionHeader, nameptr[i]);
                                    nametable[i] =ReadNullEndedStringFromPEFile((UInt32)start);
                                }
                                exportdir.ExportNameTable = nametable;
                            }

                            ///////////////////////////////////////
                            _ExportDirectory = exportdir;
                            return exportdir;
                        }
                        else throw new MemoryAllocationException();
                    }
                    else throw new ErrorReadingDictionaryDataException();
                } throw new ErrorReadingDictionaryDataException();
            }
            else throw new NoExportDirectoryException();
        }
        //////////////////////////////////////////////////////////////////////////////////

        // different on 32 and 64 bit
        private PEStructures.IMAGE_IMPORT_DIRECTORY getImportDirectory()
        {
            GetSectionsRawData();
            int index = (int)PEStructures.IMAGE_DIRECTORY_TYPES_ENUM.IMAGE_DIRECTORY_ENTRY_IMPORT;
            if (NTHeader.OptionalHeader.DataDirectory[index].VirtualAddress != 0x00)
            {
                List<PEStructures.IMAGE_IMPORT_DIRECTORY_RECORD> recordList = new List<PEStructures.IMAGE_IMPORT_DIRECTORY_RECORD>();
                int size = Marshal.SizeOf(typeof(PEStructures.IMAGE_IMPORT_DESCRIPTOR));
                long start = RelativeVirtualAddresstoPhysicalAddress(SectionHeader, NTHeader.OptionalHeader.DataDirectory[index].VirtualAddress);
                do
                {
                    PEStructures.IMAGE_IMPORT_DIRECTORY_RECORD importdir = new PEStructures.IMAGE_IMPORT_DIRECTORY_RECORD();
                    long temp = start;
                    if (start >= 0)
                    {
                        FileStream.Seek(start, SeekOrigin.Begin);
                        byte[] buffer = new byte[size];
                        int read = FileStream.Read(buffer, 0, size);
                        if (read == size)
                        {
                            GCHandle hndl = GCHandle.Alloc(buffer, GCHandleType.Pinned);
                            if (hndl.IsAllocated)
                            {
                                importdir.ImportDescriptor = (PEStructures.IMAGE_IMPORT_DESCRIPTOR)Marshal.PtrToStructure(hndl.AddrOfPinnedObject(), typeof(PEStructures.IMAGE_IMPORT_DESCRIPTOR));
                                Boolean getname = false;
                                if (importdir.ImportDescriptor.Name > 0)
                                {
                                    start = RelativeVirtualAddresstoPhysicalAddress(SectionHeader, importdir.ImportDescriptor.Name);
                                    getname = true;
                                }

                                if (getname)
                                {
                                    importdir.Name = ReadNullEndedStringFromPEFile((UInt32)start);
                                }

                                start = RelativeVirtualAddresstoPhysicalAddress(SectionHeader, importdir.ImportDescriptor.Characteristics);
                                FileStream.Seek(start, SeekOrigin.Begin);
                                List<UInt32> lookuplist = new List<UInt32>();
                                List<UInt16> ordlist = new List<UInt16>();
                                List<PEStructures.IMAGE_IMPORT_HINT_NAME> namelist = new List<PEStructures.IMAGE_IMPORT_HINT_NAME>();
                                size = 4;
                                UInt32 v = 0;
                                do
                                {
                                    byte[] buff = new byte[size];
                                    FileStream.Read(buff, 0, size);
                                    v = BitConverter.ToUInt32(buff, 0);
                                    if (v != 0x00)
                                    {
                                        lookuplist.Add(v);
                                        if ((v & 0x80000000) == 0x80000000)
                                        {
                                            ordlist.Add(BitConverter.ToUInt16(buff, 0));
                                        }
                                        else
                                        {
                                            long loc = FileStream.Position;

                                            PEStructures.IMAGE_IMPORT_HINT_NAME hintname = new PEStructures.IMAGE_IMPORT_HINT_NAME();
                                            UInt32 tempadd = (UInt32)RelativeVirtualAddresstoPhysicalAddress(SectionHeader, v);
                                            FileStream.Seek(tempadd, SeekOrigin.Begin);
                                            byte[] hint = new byte[2];
                                            FileStream.Read(hint, 0, 2);
                                            hintname.Hint = BitConverter.ToUInt16(hint, 0);
                                            hintname.Name = ReadNullEndedStringFromPEFile(tempadd + 2);
                                            namelist.Add(hintname);

                                            FileStream.Seek(loc, SeekOrigin.Begin);
                                        }
                                    }
                                } while (v != 0x00);
                                importdir.ImportLookupTable = lookuplist.ToArray();
                                importdir.ImportByOrdinalTable = ordlist.ToArray();
                                importdir.ImportByNameTable = namelist.ToArray();
                                recordList.Add(importdir);
                                ///////////////////////////////////////
                            }
                            else throw new MemoryAllocationException();
                        }
                        else throw new ErrorReadingDictionaryDataException();
                    }
                    else throw new ErrorReadingDictionaryDataException();

                    start = temp;
                    start += Marshal.SizeOf(typeof(PEStructures.IMAGE_IMPORT_DESCRIPTOR));
                    FileStream.Seek(start, SeekOrigin.Begin);
                    byte[] buff2 = new byte[1];
                    FileStream.Read(buff2, 0, 1);
                    if (buff2[0] == 0x00)
                    {
                        PEStructures.IMAGE_IMPORT_DIRECTORY dir = new PEStructures.IMAGE_IMPORT_DIRECTORY();
                        dir.ImportDirectories = recordList.ToArray();
                        _ImportDirectory = dir;
                        return dir;
                    }
                } while (true);
            }
            else throw new NoImportDirectoryException();
        }
        //////////////////////////////////////////////////////////////////////////////////

        private PEStructures.IMAGE_EXCEPTION getExceptionDirectoryx86()
        {
            GetSectionsRawData();
            PEStructures.IMAGE_EXCEPTION exp = new PEStructures.IMAGE_EXCEPTION();
            exp.Is32Bit = true;

            int index = (int)PEStructures.IMAGE_DIRECTORY_TYPES_ENUM.IMAGE_DIRECTORY_ENTRY_EXCEPTION;
            if (NTHeader.OptionalHeader.DataDirectory[index].VirtualAddress != 0x00)
            {
                int size = Marshal.SizeOf(typeof(PEStructures.IMAGE_EXCEPTION_X86));
                List<PEStructures.IMAGE_EXCEPTION_X86> table = new List<PEStructures.IMAGE_EXCEPTION_X86>();
                long start = RelativeVirtualAddresstoPhysicalAddress(SectionHeader, NTHeader.OptionalHeader.DataDirectory[index].VirtualAddress);
                if (start >= 0)
                {
                    FileStream.Seek(start, SeekOrigin.Begin);
                    byte[] buffer = new byte[size];
                    do
                    {
                        int read = FileStream.Read(buffer, 0, size);
                        if (buffer[0] != 0x00)
                        {
                            if (read == size)
                            {
                                GCHandle hndl = GCHandle.Alloc(buffer, GCHandleType.Pinned);
                                if (hndl.IsAllocated)
                                {
                                    table.Add((PEStructures.IMAGE_EXCEPTION_X86)Marshal.PtrToStructure(hndl.AddrOfPinnedObject(), typeof(PEStructures.IMAGE_EXCEPTION_X86)));
                                    hndl.Free();
                                }
                                else throw new MemoryAllocationException();
                            }
                            else throw new ErrorReadingDictionaryDataException();
                        }
                    } while (buffer[0] != 0x00);
                    exp.Exception_32Bit=table.ToArray();
                    _ExceptionDirectory = exp;
                    return exp;
                }
                else throw new ErrorReadingDictionaryDataException();
            }
            else throw new NoExceptionDirectoryException();
        }
        //////////////////////////////////////////////////////////////////////////////////

        private PEStructures.IMAGE_RELOCATION_ENTRY[] getRelocationDirectory()
        {
            GetSectionsRawData();
            int index = (int)PEStructures.IMAGE_DIRECTORY_TYPES_ENUM.IMAGE_DIRECTORY_ENTRY_BASERELOC;
            if (NTHeader.OptionalHeader.DataDirectory[index].VirtualAddress != 0x00)
            {
                long dirSize = NTHeader.OptionalHeader.DataDirectory[index].Size;
                List<PEStructures.IMAGE_RELOCATION_ENTRY> table = new List<PEStructures.IMAGE_RELOCATION_ENTRY>();
                long start = RelativeVirtualAddresstoPhysicalAddress(SectionHeader, NTHeader.OptionalHeader.DataDirectory[index].VirtualAddress);
                long sizenumber = 0;
                if (start >= 0)
                {
                    FileStream.Seek(start, SeekOrigin.Begin);
                    byte[] buffer = new byte[8];
                    do
                    {
                        buffer = new byte[8];
                        PEStructures.IMAGE_RELOCATION_ENTRY rel = new PEStructures.IMAGE_RELOCATION_ENTRY();
                        int read = FileStream.Read(buffer, 0, 8);
                        rel.PageRVA = BitConverter.ToUInt32(buffer, 0);
                        rel.BlockSize = BitConverter.ToUInt32(buffer, 4);
                        sizenumber += rel.BlockSize;
                        List<UInt16> typelist = new List<UInt16>();
                        int numbers = (int)((rel.BlockSize - 8) / 2);
                        int counter = 1;
                        byte[] tp = new byte[2];
                        do
                        {
                            read = FileStream.Read(tp, 0, 2);
                            if (read == 2)
                            {
                                typelist.Add(BitConverter.ToUInt16(tp, 0));
                            }
                            else throw new ErrorReadingDictionaryDataException();
                            counter++;
                        } while (counter <= numbers);
                        rel.Types = typelist.ToArray();
                        table.Add(rel);

                    } while (sizenumber < dirSize);
                    _RelocationDirectory = table.ToArray();
                    return _RelocationDirectory;
                }
                else throw new ErrorReadingDictionaryDataException();
            }
            else throw new NoExceptionDirectoryException();
        }
        //////////////////////////////////////////////////////////////////////////////////

        // 32 bit
        private PEStructures.IMAGE_TLS_DIRECTORY getTLSDirectory()
        {
            GetSectionsRawData();
            int index = (int)PEStructures.IMAGE_DIRECTORY_TYPES_ENUM.IMAGE_DIRECTORY_ENTRY_TLS;
            if (NTHeader.OptionalHeader.DataDirectory[index].VirtualAddress != 0x00)
            {
                int size = Marshal.SizeOf(typeof(PEStructures.IMAGE_TLS_DIRECTORY_HEADER32));
                long start = RelativeVirtualAddresstoPhysicalAddress(SectionHeader, NTHeader.OptionalHeader.DataDirectory[index].VirtualAddress);
                if (start >= 0)
                {
                    FileStream.Seek(start, SeekOrigin.Begin);
                    byte[] buffer = new byte[size];
                    FileStream.Read(buffer, 0, size);
                    
                    PEStructures.IMAGE_TLS_DIRECTORY tlsdir = new PEStructures.IMAGE_TLS_DIRECTORY();
                    tlsdir.Is32Bit = true;

                    GCHandle hndl = GCHandle.Alloc(buffer, GCHandleType.Pinned);
                    if (hndl.IsAllocated)
                    {
                        tlsdir.Header = (PEStructures.IMAGE_TLS_DIRECTORY_HEADER32)Marshal.PtrToStructure(hndl.AddrOfPinnedObject(), typeof(PEStructures.IMAGE_TLS_DIRECTORY_HEADER32));
                        _TLSDirectory = tlsdir;
                        return tlsdir;
                    }
                    else throw new MemoryAllocationException();
                }
                else throw new ErrorReadingDictionaryDataException();
            }
            else throw new NoExceptionDirectoryException();
        }
        //////////////////////////////////////////////////////////////////////////////////

        // 32 bit
        private PEStructures.IMAGE_LOAD_CONFIG_DIRECTORY getLoadConfigDirectory()
        {
            GetSectionsRawData();
            int index = (int)PEStructures.IMAGE_DIRECTORY_TYPES_ENUM.IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG;
            if (NTHeader.OptionalHeader.DataDirectory[index].VirtualAddress != 0x00)
            {
                int size = Marshal.SizeOf(typeof(PEStructures.IMAGE_LOAD_CONFIG_DIRECTORY32));
                long start = RelativeVirtualAddresstoPhysicalAddress(SectionHeader, NTHeader.OptionalHeader.DataDirectory[index].VirtualAddress);
                if (start >= 0)
                {
                    FileStream.Seek(start, SeekOrigin.Begin);
                    byte[] buffer = new byte[size];
                    FileStream.Read(buffer, 0, size);
                    PEStructures.IMAGE_LOAD_CONFIG_DIRECTORY32 ldcfg = new PEStructures.IMAGE_LOAD_CONFIG_DIRECTORY32();
                    GCHandle hndl = GCHandle.Alloc(buffer, GCHandleType.Pinned);
                    if (hndl.IsAllocated)
                    {
                        ldcfg = (PEStructures.IMAGE_LOAD_CONFIG_DIRECTORY32)Marshal.PtrToStructure(hndl.AddrOfPinnedObject(), typeof(PEStructures.IMAGE_LOAD_CONFIG_DIRECTORY32));

                        PEStructures.IMAGE_LOAD_CONFIG_DIRECTORY mainldcfg = new PEStructures.IMAGE_LOAD_CONFIG_DIRECTORY();
                        mainldcfg.Is32Bit = true;
                        mainldcfg.LoadConfig_32Bit = ldcfg;
                        _LoadConfigDirectory = mainldcfg;
                        return mainldcfg;
                    }
                    else throw new MemoryAllocationException();
                }
                else throw new ErrorReadingDictionaryDataException();
            }
            else throw new NoTLSDirectoryException();
        }
        //////////////////////////////////////////////////////////////////////////////////

        // 32 bit
        private PEStructures.IMAGE_CLR getCLRDirectory()
        {
            GetSectionsRawData();
            int index = (int)PEStructures.IMAGE_DIRECTORY_TYPES_ENUM.IMAGE_DIRECTORY_ENTRY_COM_CLR_DESCRIPTOR;
            if (NTHeader.OptionalHeader.DataDirectory[index].VirtualAddress != 0x00)
            {
                PEStructures.IMAGE_CLR clr = new PEStructures.IMAGE_CLR();
                int size = Marshal.SizeOf(typeof(PEStructures.IMAGE_COR20_HEADER));
                long start = RelativeVirtualAddresstoPhysicalAddress(SectionHeader, NTHeader.OptionalHeader.DataDirectory[index].VirtualAddress);
                if (start >= 0)
                {
                    FileStream.Seek(start, SeekOrigin.Begin);
                    byte[] buffer = new byte[size];
                    FileStream.Read(buffer, 0, size);
                    GCHandle hndl = GCHandle.Alloc(buffer, GCHandleType.Pinned);
                    if (hndl.IsAllocated)
                    {
                        clr.Core20Header = (PEStructures.IMAGE_COR20_HEADER)Marshal.PtrToStructure(hndl.AddrOfPinnedObject(), typeof(PEStructures.IMAGE_COR20_HEADER));
                        _CLRDirectory = clr;
                        return clr;
                    }
                    else throw new MemoryAllocationException();
                }
                else throw new ErrorReadingDictionaryDataException();
            }
            else throw new NoCLRDirectoryException();
        }
        //////////////////////////////////////////////////////////////////////////////////

        private PEStructures.IMAGE_RESOURCE_DIRECTORY getResourceDirectory()
        {
            GetSectionsRawData();
            int index = (int)PEStructures.IMAGE_DIRECTORY_TYPES_ENUM.IMAGE_DIRECTORY_ENTRY_RESOURCE;
            if (NTHeader.OptionalHeader.DataDirectory[index].VirtualAddress != 0x00)
            {
                PEStructures.IMAGE_RESOURCE_DIRECTORY rsc =
                    getHelper_ResourceDirectoryTable((UInt32)RelativeVirtualAddresstoPhysicalAddress(SectionHeader, NTHeader.OptionalHeader.DataDirectory[index].VirtualAddress));
                _ResourceDirectory = rsc;
                return rsc;
            }
            else throw new NoResourceDirectoryException();
        }

        private PEStructures.IMAGE_RESOURCE_DIRECTORY getHelper_ResourceDirectoryTable(UInt32 rva)
        {
            PEStructures.IMAGE_RESOURCE_DIRECTORY table = new PEStructures.IMAGE_RESOURCE_DIRECTORY();
            int size = Marshal.SizeOf(typeof(PEStructures.IMAGE_RESOURCE_DIRECTORY_TABLE));
            long start = rva;//(addressIsRVA ? rva : (UInt32)RelativeVirtualAddresstoPhysicalAddress(SectionHeader, rva));
            if (start >= 0)
            {
                FileStream.Seek(start, SeekOrigin.Begin);
                byte[] buffer = new byte[size];
                FileStream.Read(buffer, 0, size);
                GCHandle hndl = GCHandle.Alloc(buffer, GCHandleType.Pinned);
                if (hndl.IsAllocated)
                {
                    table.NodeType = PEStructures.ResourceNodeType.ResourceDirectoryTable;
                    table.ResourceDirectoryTable = (PEStructures.IMAGE_RESOURCE_DIRECTORY_TABLE)Marshal.PtrToStructure(hndl.AddrOfPinnedObject(), typeof(PEStructures.IMAGE_RESOURCE_DIRECTORY_TABLE));
                    table.NextLevelNodes = getHelper_ResourceDirectoryTableEntry(rva + (UInt32)size,
                        table.ResourceDirectoryTable.NumberOfNamedEntries + table.ResourceDirectoryTable.NumberOfIdEntries);
                    return table;
                }
                else throw new MemoryAllocationException();
            }
            else throw new ErrorReadingDictionaryDataException();
        }

        private PEStructures.IMAGE_RESOURCE_DIRECTORY[] getHelper_ResourceDirectoryTableEntry(UInt32 rva, int entryCount)
        {
            List<PEStructures.IMAGE_RESOURCE_DIRECTORY> lst = new List<PEStructures.IMAGE_RESOURCE_DIRECTORY>();
            int size = Marshal.SizeOf(typeof(PEStructures.IMAGE_RESOURCE_DIRECTORY_TABLE_ENTRY_INTERNAL));
            long start = rva; //(addressIsRVA ? address : (UInt32)RelativeVirtualAddresstoPhysicalAddress(SectionHeader, address));
            long rscStart = RelativeVirtualAddresstoPhysicalAddress(
                SectionHeader,
                NTHeader.OptionalHeader.DataDirectory[(int)PEStructures.IMAGE_DIRECTORY_TYPES_ENUM.IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);

            if (start >= 0)
            {
                for (int i = 0; i < entryCount; i++)
                {
                    FileStream.Seek(start, SeekOrigin.Begin);
                    byte[] buffer = new byte[size];
                    FileStream.Read(buffer, 0, size);
                    GCHandle hndl = GCHandle.Alloc(buffer, GCHandleType.Pinned);
                    if (hndl.IsAllocated)
                    {
                        PEStructures.IMAGE_RESOURCE_DIRECTORY ent = new PEStructures.IMAGE_RESOURCE_DIRECTORY();
                        ent.NodeType = PEStructures.ResourceNodeType.ResourceDirectoryEntry;

                        PEStructures.IMAGE_RESOURCE_DIRECTORY_TABLE_ENTRY_INTERNAL entInt =
                            (PEStructures.IMAGE_RESOURCE_DIRECTORY_TABLE_ENTRY_INTERNAL)
                            Marshal.PtrToStructure(hndl.AddrOfPinnedObject(), typeof(PEStructures.IMAGE_RESOURCE_DIRECTORY_TABLE_ENTRY_INTERNAL));

                        if ((entInt.NameRVA & 0x80000000) != 0)
                        {
                            ent.TableEntry.IsNamed = true;
                            ent.TableEntry.NameRVA = entInt.NameRVA;

                            FileStream.Seek((UInt32)(entInt.NameRVA & 0x7FFFFFFF) + (UInt32)rscStart, SeekOrigin.Begin);
                            byte[] b2 = new byte[2];
                            FileStream.Read(b2, 0, 2);
                            UInt16 len = BitConverter.ToUInt16(b2, 0);
                            ent.TableEntry.Name = ReadLimitedStringFromPEFile((UInt32)(entInt.NameRVA & 0x7FFFFFFF) + (UInt32)rscStart + 2, len, 2);
                        }
                        else
                        {
                            ent.TableEntry.IsNamed = false;
                            ent.TableEntry.NameRVA = 0;
                            ent.TableEntry.IntegerID = entInt.IntegerID;
                            ent.TableEntry.Name = "";
                        }

                        if ((entInt.DataEntryRVA & 0x80000000) != 0)
                        {
                            ent.NextLevelNodes = new PEStructures.IMAGE_RESOURCE_DIRECTORY[] { 
                                getHelper_ResourceDirectoryTable((UInt32)(entInt.DataEntryRVA & 0x7FFFFFFF) + (UInt32)rscStart) };
                        }
                        else
                        {
                            ent.NextLevelNodes = new PEStructures.IMAGE_RESOURCE_DIRECTORY[] { getHelper_ResourceData((UInt32)(entInt.DataEntryRVA) + (UInt32)rscStart) };
                        }

                        lst.Add(ent);
                    }
                    else throw new MemoryAllocationException();
                    start += size;
                }
                return lst.ToArray();
            }
            else throw new ErrorReadingDictionaryDataException();
        }

        private PEStructures.IMAGE_RESOURCE_DIRECTORY getHelper_ResourceData(UInt32 rva)
        {
            PEStructures.IMAGE_RESOURCE_DIRECTORY data = new PEStructures.IMAGE_RESOURCE_DIRECTORY();
            int size = Marshal.SizeOf(typeof(PEStructures.IMAGE_RESOURCE_DATA_ENTRY));
            long start = rva; //(addressIsRVA ? address : (UInt32)RelativeVirtualAddresstoPhysicalAddress(SectionHeader, address));
            long rscStart = RelativeVirtualAddresstoPhysicalAddress(
                SectionHeader,
                NTHeader.OptionalHeader.DataDirectory[(int)PEStructures.IMAGE_DIRECTORY_TYPES_ENUM.IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);

            if (start >= 0)
            {
                FileStream.Seek(start, SeekOrigin.Begin);
                byte[] buffer = new byte[size];
                FileStream.Read(buffer, 0, size);
                GCHandle hndl = GCHandle.Alloc(buffer, GCHandleType.Pinned);
                if (hndl.IsAllocated)
                {
                    data.NodeType = PEStructures.ResourceNodeType.ResourceDataEntry;
                    data.ResourceData.DataEntry = (PEStructures.IMAGE_RESOURCE_DATA_ENTRY)Marshal.PtrToStructure(hndl.AddrOfPinnedObject(), typeof(PEStructures.IMAGE_RESOURCE_DATA_ENTRY));
                    hndl.Free();

                    buffer = new byte[data.ResourceData.DataEntry.Size];
                    FileStream.Seek(RelativeVirtualAddresstoPhysicalAddress(SectionHeader, data.ResourceData.DataEntry.OffsetToData), SeekOrigin.Begin);
                    int read = FileStream.Read(buffer, 0, (int)data.ResourceData.DataEntry.Size);
                    if ((UInt32)read == data.ResourceData.DataEntry.Size)
                    {
                        data.ResourceData.Data = buffer;
                        return data;
                    }
                    else throw new ErrorReadingResourceDataException();
                }
                else throw new MemoryAllocationException();
            }
            else throw new ErrorReadingDictionaryDataException();
        }

        //////////////////////////////////////////////////////////////////////////////////

        #endregion

        #region COMMON FUNCTIONS

        private string ReadNullEndedStringFromPEFile(UInt32 pa)
        {
            List<byte> lst = new List<byte>();
            byte[] buff = new byte[1];
            FileStream.Seek(pa, SeekOrigin.Begin);
            do
            {
                FileStream.Read(buff, 0, 1);
                if (buff[0] != 0x00) lst.Add(buff[0]);
            } while (buff[0] != 0x00);
            return System.Text.ASCIIEncoding.ASCII.GetString(lst.ToArray());
        }
        //////////////////////////////////////////////////////////////////////////////////

        private string ReadLimitedStringFromPEFile(UInt32 pa, int len, int sizeMultiple = 1)
        {
            byte[] buff = new byte[len * sizeMultiple];
            FileStream.Seek(pa, SeekOrigin.Begin);
            FileStream.Read(buff, 0, len * sizeMultiple);
            if (sizeMultiple == 1)
                return System.Text.ASCIIEncoding.ASCII.GetString(buff);
            else return System.Text.UnicodeEncoding.Unicode.GetString(buff);
        }
        //////////////////////////////////////////////////////////////////////////////////

        private int RelativeVirtualAddresstoPhysicalAddress(PEStructures.IMAGE_SECTION_HEADER_RECORD[] sections, UInt32 rva)
        {
            foreach (PEStructures.IMAGE_SECTION_HEADER_RECORD r in sections)
            {
                if (rva >= r.VirtualAddress && rva <= (r.VirtualAddress + r.VirtualSize))
                {
                    return (int)(r.PointerToRawData + (rva - r.VirtualAddress));
                }
            }
            return -1;
        }
        //////////////////////////////////////////////////////////////////////////////////

        #endregion
    }
}
